`timescale 1ns/1ps
`default_nettype none

/* NOTE:
*  - 串移数据
*/

module data_shift
    #(
    parameter   DW      = 96
    )
    (
    // system signal
    input  wire         I_sclk,  // 125M
    input  wire         I_rst_n,
    // config
    input  wire [7:0]   I_cfg_clock_low,     // 时钟低电平时钟数
    input  wire [7:0]   I_cfg_clock_cycle,   // 时钟整周期时钟数
    input  wire [7:0]   I_cfg_clock_phase,   // 时钟相位
    input  wire         I_cfg_data_polarity, // 数据极性
    // shift request
    input  wire         I_shift_start,    // 串移开始
    output wire         O_shift_done,     // 串移结束
    input  wire         I_shift_mode,     // 0 - reg, 1 - pixel
    input  wire [3:0]   I_load_num,       // load包含clk数
    input  wire         I_shift_ram_sel,  // 读取ram起始地址
    input  wire [9:0]   I_shift_length,   // 串移长度
    // column addr table
    output wire         O_col_addr_req,
    output wire [8:0]   O_col_addr_index,
    input  wire [8:0]   I_col_addr_data,
    // data buffer
    output wire         O_data_ram_rden,
    output wire [9:0]   O_data_ram_addr,
    input  wire [DW-1:0] I_data_ram_q,
    // config data
    output wire         O_config_ack,
    input  wire [DW-1:0] I_config_data,
    // data out
    output wire         O_shift_clock,
    output wire [DW-1:0] O_shift_data,
    output wire         O_shift_load
);
//------------------------Parameter----------------------
localparam
  //SL = 4;  // 时钟输出延时时间,delay=1
    SL = 7;  // 时钟输出延时时间,delay=4

//------------------------Local signal-------------------
// clock output
reg  [7:0]      clk_cnt;
reg             clk_out;
reg  [SL:0]     clk_sr;
reg             clk_en;
reg             clk_ext;

// data output
reg             read_ram;
reg  [9:0]      data_cnt;
reg  [DW-1:0]   data_buf0;
reg  [DW-1:0]   data_buf1;
reg             ram_sel;
reg  [8:0]      pixel_id;
reg             addr_req;
reg             data_req;
reg  [SL-4:0]   ram_rden_sr;
reg             config_ack;

// load output
reg  [3:0]      load_num;
reg             load_out;
reg  [SL:1]     load_sr;

// misc
reg             shift_done;

//------------------------Instantiation------------------

//------------------------Body---------------------------
//{{{+++++++++++++++++++++clock output+++++++++++++++++++
assign O_shift_clock = clk_sr[SL];

// clk_cnt
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        clk_cnt <= 1'b1;
    else if (!clk_en && !clk_ext)
        clk_cnt <= 1'b1;
    else if (clk_cnt == I_cfg_clock_cycle)
        clk_cnt <= 1'b1;
    else
        clk_cnt <= clk_cnt + 1'b1;
end

// clk_out
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        clk_out <= 1'b0;
    else if (~clk_en)
        clk_out <= 1'b0;
    else if (clk_cnt == I_cfg_clock_low)
        clk_out <= 1'b1;
    else if (clk_cnt == I_cfg_clock_cycle)
        clk_out <= 1'b0;
end

// clk_sr
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        clk_sr <= 1'b0;
    else
        clk_sr <= {clk_sr[SL-1:0], clk_out};
end

// clk_en
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        clk_en <= 1'b0;
    else if (I_shift_start)
        clk_en <= 1'b1;
    else if (clk_cnt == I_cfg_clock_cycle && data_cnt == 1'b1)
        clk_en <= 1'b0;
end

// clk_ext
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        clk_ext <= 1'b0;
    else if (clk_cnt == I_cfg_clock_cycle) begin
        if (data_cnt == 1'b1)
            clk_ext <= 1'b1;
        else if (data_cnt == 10'h3ff)
            clk_ext <= 1'b0;
    end
end
//}}}++++++++++++++++++++++++++++++++++++++++++++++++++++

//{{{+++++++++++++++++++++data output++++++++++++++++++++
assign O_shift_data     = data_buf1;
assign O_col_addr_req   = addr_req;
assign O_col_addr_index = pixel_id;
assign O_data_ram_rden  = data_req;
assign O_data_ram_addr  = {ram_sel, I_col_addr_data};

assign O_config_ack = config_ack;

// read_ram
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        read_ram <= 1'b0;
    else if (I_shift_start)
        read_ram <= I_shift_mode;
end

// data_cnt
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        data_cnt <= 1'b0;
    else if (I_shift_start)
        data_cnt <= I_shift_length;
    else if (clk_cnt == I_cfg_clock_cycle)
        data_cnt <= data_cnt - 1'b1;
end

// data_buf0
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        data_buf0 <= 1'b0;
    else if(ram_rden_sr[SL-4])
        data_buf0 <= I_data_ram_q;
end

// data_buf1
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        data_buf1 <= 1'b0;
    else if (read_ram) begin
        if (I_cfg_data_polarity)
            data_buf1 <= data_buf0;
        else
            data_buf1 <= ~data_buf0;
    end
    else if (config_ack)
        data_buf1 <= I_config_data;
end

// ram_sel
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        ram_sel <= 1'b0;
    else if (I_shift_start)
        ram_sel <= I_shift_ram_sel;
end

// pixel_id
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        pixel_id <= 1'b0;
    else if (I_shift_start)
        pixel_id <= 1'b0;
    else if (addr_req)
        pixel_id <= pixel_id + 1'b1;
end

// addr_req
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        addr_req <= 1'b0;
    else if (clk_en && clk_cnt == I_cfg_clock_phase)
        addr_req <= 1'b1;
    else
        addr_req <= 1'b0;
end

// data_req
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        data_req <= 1'b0;
    else
        data_req <= addr_req;
end

// config_ack
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        config_ack <= 1'b0;
    else if (!read_ram)
        config_ack <= ram_rden_sr[SL-4];
end

//ram_rden_sr[SL-4:0]
always@(posedge I_sclk or negedge I_rst_n)
    if(!I_rst_n)
        ram_rden_sr <= 'b0;
    else
        ram_rden_sr <= {ram_rden_sr[SL-5:0],O_data_ram_rden};
//}}}++++++++++++++++++++++++++++++++++++++++++++++++++++

//{{{+++++++++++++++++++++load output++++++++++++++++++++
assign O_shift_load = load_sr[SL];

// load_num
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        load_num <= 1'b0;
    else if (I_shift_start)
        load_num <= I_load_num;
end

// load_out
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        load_out <= 1'b0;
    else if ((clk_en || clk_ext) && data_cnt == load_num)
        load_out <= 1'b1;
    else if (clk_ext && data_cnt == 10'h3ff)
        load_out <= 1'b0;
end

// load_sr
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        load_sr <= 1'b0;
    else
        load_sr <= {load_sr[SL-1:1], load_out};
end
//}}}++++++++++++++++++++++++++++++++++++++++++++++++++++

//{{{+++++++++++++++++++++misc+++++++++++++++++++++++++++
assign O_shift_done = shift_done;

// shift_done
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        shift_done <= 1'b0;
    else if (clk_cnt == I_cfg_clock_cycle && data_cnt == 10'h3ff)
        shift_done <= 1'b1;
    else
        shift_done <= 1'b0;
end
//}}}++++++++++++++++++++++++++++++++++++++++++++++++++++

endmodule

`default_nettype wire

// vim:set ts=4 sw=4 et fenc=utf-8 fdm=marker:
